<?php

namespace common\db;

use Yii;
use yii\base\InvalidConfigException;
use yii\db\ActiveQuery;
use yii\helpers\ArrayHelper;

/**
 * 分表AR基础模型
 *
 * @author emhome<emhome@163.com>
 * @since 2.0
 */
class SequenceActiveRecord extends ActiveRecord {

    /**
     * @var string 分表数据表名 
     */
    public static $tablename = '';

    /**
     * @var string 分表索引
     */
    private static $partitionIndex = null;

    /**
     * @var string 分表字段名
     */
    public static $sequenceField = '';

    /**
     * @var string 分表字段值
     */
    private static $sequenceFieldValue = null;

    /**
     * @var string 分割符
     */
    const DELIMITER_CHAR = '_';

    /**
     * Mar constructor.
     * @param array $index
     * @param array $config
     */
    public function __construct($index = 0, $config = []) {
        self::resetPartitionIndex($index);
        parent::__construct($config);
    }

    /**
     * @return array|string
     */
    public static function tableName() {
        return '{{%' . static::$tablename . static::DELIMITER_CHAR . ltrim(self::$partitionIndex, static::DELIMITER_CHAR) . '}}';
    }

    /**
     * 重置分区id
     * @param unknown $index
     */
    private static function resetPartitionIndex($index = null) {
        $options = ArrayHelper::getValue(Yii::$app->params, 'sequence');
        if (!$options) {
            throw new InvalidConfigException('"' . get_called_class() . '" sequence options must be set.');
        }
        $partitionCount = ArrayHelper::getValue($options, ['table', static::$tablename]);
        if ($index && $partitionCount) {
            self::$partitionIndex = $index % $partitionCount;
            self::$sequenceFieldValue = $index;
        }
    }

    /**
     * 分表查询
     * @param $index
     * @return ActiveQuery
     */
    public static function findx($index) {
        self::resetPartitionIndex($index);
        $query = Yii::createObject(ActiveQuery::className(), [get_called_class(), ['from' => [static::tableName()]]]);
        /* @var $query ActiveQuery */
        return self::bindSequenceMasterCondition($query);
    }

    /**
     * 构建分表查询基础条件
     * @param $query
     * @return ActiveQuery
     */
    public static function bindSequenceMasterCondition(&$query) {
        $query->andWhere([static::$sequenceField => self::$sequenceFieldValue]);
        return $query;
    }

    /**
     * instantiate
     * @param array $row
     * @return static
     */
    public static function instantiate($row) {
        return new static(self::$partitionIndex);
    }

    /**
     * 分表单模查询
     * @param $table
     * @param $condition
     * @return mixed
     */
    public static function findOnex($table, $condition) {
        return static::findByConditionx($table, $condition)->one();
    }

    /**
     * 分表批量查询
     * @param $table
     * @param $condition
     * @return mixed
     */
    public static function findAllx($table, $condition) {
        return static::findByConditionx($table, $condition)->all();
    }

    /**
     * 分表条件查询
     * @param $table
     * @param $condition
     * @return mixed
     */
    protected static function findByConditionx($table, $condition) {
        $query = static::findx($table);

        if (!ArrayHelper::isAssociative($condition)) {
            // query by primary key
            $primaryKey = static::primaryKey();
            if (isset($primaryKey[0])) {
                $condition = [$primaryKey[0] => $condition];
            } else {
                throw new InvalidConfigException('"' . get_called_class() . '" must have a primary key.');
            }
        }

        return $query->andWhere($condition);
    }

    /**
     * @param $insert
     */
    public function beforeSave($insert) {
        if (!parent::beforeSave($insert)) {
            return false;
        }
        if ($insert) {
            $this->generateSequenceId();
            $field = static::$sequenceField;
            $this->$field = self::$sequenceFieldValue;
        }
        return true;
    }

    /**
     * 生成分表记录的主键
     * @return mixed
     */
    private function generateSequenceId() {
        $procedureName = ArrayHelper::getValue(Yii::$app->params, 'sequence.name');
        if (!$procedureName) {
            throw new InvalidConfigException('"' . get_called_class() . '" must has a primary key generation.');
        }
        $sql = 'CALL ' . $procedureName . '("' . static::$tablename . '")';
        return $this->id = static::getDb()->createCommand($sql)->queryScalar();
    }

    /**
     * 分表批量删除
     * @param $table
     * @param $condition
     * @param $params
     * @return mixed
     */
    public static function deleteAllX($table, $condition = [], $params = []) {
        self::resetPartitionIndex($table);
        return static::deleteAll($condition, $params);
    }

    /**
     * 分表批量软删除
     * @param $table
     * @param $attributes
     * @param $condition
     * @return mixed
     */
    public static function softDeleteAllX($table, $attributes = '', $condition = []) {
        self::resetPartitionIndex($table);
        if ($attributes) {
            return static::updateAll($attributes, $condition);
        }
        return static::updateAll(['deleted_at' => time()], $condition);
    }

}
